package Thread.BlockingQueue;
//自主实现一个阻塞队列
//此案例中不考虑泛型参数，只是基于String进行一个数组存储

class MyBlockingQueue{
    private String[] data=null;
    private volatile int head=0;
    private volatile int tail=0;
    private volatile int size=0;
    public MyBlockingQueue(int capacity){
        data=new String[capacity];
    }
    //一个队列不可能既是空又是满的状态，所以两边不会同时阻塞
    //阻塞队列的实现原理：
    /*在 put 的时候先判定,队列是否满了.如果满了,就通过 wait 阻塞,
    此处的 wait 由 take 操作,取出元素之后,notify 唤醒.
    在 take 的时候, 也先判定,队列是否空了.如果空了,
    就通过 wait 阻塞此处的 wait 由 put 操作,放入元素之后, notify 唤醒.
     */

    public void put(String s) throws InterruptedException {
        synchronized ( this){
            if(data.length==size){
                //队列已满
                //return;
                this.wait();
            }
            data[tail]=s;
            tail++;

            //此种写法相对于循环取余写法，代码可读性更高
            //tail=tail%data.length;
            //并且在执行效率上有提升相对于取余运算
            //计算机在乘除的操作效率慢
            if(tail>=data.length){
                //if判断是一个简单且快速的cmp指令
                tail=0;
            }
            size++;
            this.notify();
        }

    }
    public String take() throws InterruptedException {
        String s="";
        synchronized ( this){
            //把if改成while，while的作用是在wait被唤醒之后，再次确认条件，查看是否能执行
            //避免还没满足条件就执行导致出错
            while(size==0){
                //队列已空
                //return null;
                this.wait();
            }
            s=data[head];
            head++;
            if(head>=data.length){
                head=0;
            }
            size--;
            this.notify();
        }
        return s;
    }

}
public class Demo34 {
    public static void main(String[] args) {
        MyBlockingQueue queue=new MyBlockingQueue(1000);
        Thread t1=new Thread(()->{
            int i=1;
            while (true){

                try {
                    queue.put(""+i);
                    System.out.println("生产元素 "+i);
                    i++;
                    //让生产者慢一点。消费者快一点，每生产一个休息一秒
                    //虽然消费者没有写任何的sleep，但是因为阻塞队列效果，就会使得生产者和消费者同步
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }

        });
        //消费者线程
        Thread t2=new Thread(()->{
            while (true){
                try {
                    Integer i= Integer.parseInt(queue.take());
                    System.out.println("消费元素 "+i);
                    //给消费者加上sleep，让消费慢点
                    //Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t1.start();
        t2.start();
    }
}
